xend/xm: Add PSCSI_HBA class and DSCSI_HBA class to XenAPI
authorKeir Fraser <keir.fraser@citrix.com>
Thu, 12 Nov 2009 11:40:44 +0000 (11:40 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Thu, 12 Nov 2009 11:40:44 +0000 (11:40 +0000)
XenAPI (not xapi) has supported only LUN assignment mode for pvSCSI.
But at last, HOST assignment mode also is supported by these patches.
To support HOST assignment mode, these patches add PSCSI_HBA class
and DSCSI_HBA class to XenAPI.

Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
tools/python/xen/xend/XendAPI.py
tools/python/xen/xend/XendConfig.py
tools/python/xen/xend/XendDSCSI.py
tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xend/XendNode.py
tools/python/xen/xend/XendPSCSI.py
tools/python/xen/xm/create.dtd
tools/python/xen/xm/create.py
tools/python/xen/xm/main.py
tools/python/xen/xm/xenapi_create.py

index b7ca31705610507cd169dd571514c7e15179bb3f..06134ca7ac4193d2b792cfa645c9ea280d4cb374 100644 (file)
@@ -48,8 +48,8 @@ from XendPIF import XendPIF
 from XendPBD import XendPBD
 from XendPPCI import XendPPCI
 from XendDPCI import XendDPCI
-from XendPSCSI import XendPSCSI
-from XendDSCSI import XendDSCSI
+from XendPSCSI import XendPSCSI, XendPSCSI_HBA
+from XendDSCSI import XendDSCSI, XendDSCSI_HBA
 from XendXSPolicy import XendXSPolicy, XendACMPolicy
 
 from XendAPIConstants import *
@@ -495,7 +495,9 @@ classes = {
     'PPCI'         : valid_object("PPCI"),
     'DPCI'         : valid_object("DPCI"),
     'PSCSI'        : valid_object("PSCSI"),
-    'DSCSI'        : valid_object("DSCSI")
+    'PSCSI_HBA'    : valid_object("PSCSI_HBA"),
+    'DSCSI'        : valid_object("DSCSI"),
+    'DSCSI_HBA'    : valid_object("DSCSI_HBA"),
 }
 
 autoplug_classes = {
@@ -507,7 +509,9 @@ autoplug_classes = {
     'PPCI'        : XendPPCI,
     'DPCI'        : XendDPCI,
     'PSCSI'       : XendPSCSI,
+    'PSCSI_HBA'   : XendPSCSI_HBA,
     'DSCSI'       : XendDSCSI,
+    'DSCSI_HBA'   : XendDSCSI_HBA,
     'XSPolicy'    : XendXSPolicy,
     'ACMPolicy'   : XendACMPolicy,
 }
@@ -899,6 +903,7 @@ class XendAPI(object):
                     'PIFs',
                     'PPCIs',
                     'PSCSIs',
+                    'PSCSI_HBAs',
                     'host_CPUs',
                     'cpu_configuration',
                     'metrics',
@@ -991,6 +996,8 @@ class XendAPI(object):
         return xen_api_success(XendNode.instance().get_PPCI_refs())
     def host_get_PSCSIs(self, session, ref):
         return xen_api_success(XendNode.instance().get_PSCSI_refs())
+    def host_get_PSCSI_HBAs(self, session, ref):
+        return xen_api_success(XendNode.instance().get_PSCSI_HBA_refs())
     def host_get_host_CPUs(self, session, host_ref):
         return xen_api_success(XendNode.instance().get_host_cpu_refs())
     def host_get_metrics(self, _, ref):
@@ -1068,7 +1075,8 @@ class XendAPI(object):
                   'PIFs': XendPIF.get_all(),
                   'PBDs': XendPBD.get_all(),
                   'PPCIs': XendPPCI.get_all(),
-                  'PSCSIs': XendPSCSI.get_all()}
+                  'PSCSIs': XendPSCSI.get_all(),
+                  'PSCSI_HBAs': XendPSCSI_HBA.get_all()}
         return xen_api_success(record)
 
     def host_tmem_thaw(self, _, host_ref, cli_id):
@@ -1270,6 +1278,7 @@ class XendAPI(object):
                   'VTPMs',
                   'DPCIs',
                   'DSCSIs',
+                  'DSCSI_HBAs',
                   'tools_version',
                   'domid',
                   'is_control_domain',
@@ -1420,6 +1429,10 @@ class XendAPI(object):
         dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
         return xen_api_success(dom.get_dscsis())
 
+    def VM_get_DSCSI_HBAs(self, session, vm_ref):
+        dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
+        return xen_api_success(dom.get_dscsi_HBAs())
+
     def VM_get_tools_version(self, session, vm_ref):
         dom = XendDomain.instance().get_vm_by_uuid(vm_ref)
         return dom.get_tools_version()
@@ -1806,6 +1819,7 @@ class XendAPI(object):
             'VTPMs': xeninfo.get_vtpms(),
             'DPCIs': xeninfo.get_dpcis(),
             'DSCSIs': xeninfo.get_dscsis(),
+            'DSCSI_HBAs': xeninfo.get_dscsi_HBAs(),
             'PV_bootloader': xeninfo.info.get('PV_bootloader'),
             'PV_kernel': xeninfo.info.get('PV_kernel'),
             'PV_ramdisk': xeninfo.info.get('PV_ramdisk'),
index 4b05fe55e2ad44a60a25b3004220d9e656d5d2aa..6a168a264d144fdfc74da0ca91e8282678d8613d 100644 (file)
@@ -28,7 +28,7 @@ from xen.xend import XendAPIStore
 from xen.xend.XendPPCI import XendPPCI
 from xen.xend.XendDPCI import XendDPCI
 from xen.xend.XendPSCSI import XendPSCSI
-from xen.xend.XendDSCSI import XendDSCSI
+from xen.xend.XendDSCSI import XendDSCSI, XendDSCSI_HBA
 from xen.xend.XendError import VmError
 from xen.xend.XendDevices import XendDevices
 from xen.xend.PrettyPrint import prettyprintstring
@@ -1355,6 +1355,14 @@ class XendConfig(dict):
                 vscsi_mode = vscsi_dict['feature-host']
                 vscsi_be = vscsi_dict.get('backend', None)
 
+                # create XenAPI DSCSI_HBA objects.
+                dscsi_HBA_record = {
+                    'VM': self['uuid'],
+                    'virtual_host': int(vscsi_devs[0]['v-dev'].split(':')[0]),
+                    'assignment_mode': vscsi_mode and 'HOST' or 'LUN'
+                }
+                XendDSCSI_HBA(vscsi_devs_uuid, dscsi_HBA_record)
+
                 # create XenAPI DSCSI objects.
                 for vscsi_dev in vscsi_devs:
                     dscsi_uuid = vscsi_dev.get('uuid')
@@ -1364,6 +1372,7 @@ class XendConfig(dict):
                     dscsi_record = {
                         'VM': self['uuid'],
                         'PSCSI': pscsi_uuid,
+                        'HBA': vscsi_devs_uuid,
                         'virtual_HCTL': vscsi_dev.get('v-dev')
                     }
                     XendDSCSI(dscsi_uuid, dscsi_record)
@@ -1858,6 +1867,10 @@ class XendConfig(dict):
                     if vscsi_devid == dscsi_inst.get_virtual_host():
                         XendAPIStore.deregister(dscsi_uuid, "DSCSI")
 
+                # destroy existing XenAPI DSCSI_HBA objects
+                if not vscsi_devs:
+                    XendAPIStore.deregister(dev_uuid, 'DSCSI_HBA')
+
                 # create XenAPI DSCSI objects.
                 for vscsi_dev in vscsi_devs:
                     dscsi_uuid = vscsi_dev.get('uuid')
@@ -1867,6 +1880,7 @@ class XendConfig(dict):
                     dscsi_record = {
                         'VM': self['uuid'],
                         'PSCSI': pscsi_uuid,
+                        'HBA': dev_uuid,
                         'virtual_HCTL': vscsi_dev.get('v-dev')
                     }
                     XendDSCSI(dscsi_uuid, dscsi_record)
index 5926325b2d08e8dbae07102fd8a903de71944262..794a54f001b7720bc5abaf17b0998cb1e6058405 100644 (file)
@@ -37,6 +37,7 @@ class XendDSCSI(XendBase):
     def getAttrRO(self):
         attrRO = ['VM',
                   'PSCSI',
+                  'HBA',
                   'virtual_host',
                   'virtual_channel',
                   'virtual_target',
@@ -52,6 +53,7 @@ class XendDSCSI(XendBase):
     def getAttrInst(self):
         attrInst = ['VM',
                     'PSCSI',
+                    'HBA',
                     'virtual_HCTL']
         return XendBase.getAttrInst() + attrInst
 
@@ -120,6 +122,9 @@ class XendDSCSI(XendBase):
     def get_PSCSI(self):
         return self.PSCSI
 
+    def get_HBA(self):
+        return self.HBA
+
     def get_virtual_host(self):
         return self.virtual_host
 
@@ -172,3 +177,123 @@ class XendDSCSI(XendBase):
                               dom.destroy_dscsi, \
                               self.get_uuid())
 
+
+class XendDSCSI_HBA(XendBase):
+    """Representation of a half-virtualized SCSI HBA."""
+
+    def getClass(self):
+        return "DSCSI_HBA"
+
+    def getAttrRO(self):
+        attrRO = ['VM',
+                  'PSCSI_HBAs',
+                  'DSCSIs',
+                  'virtual_host',
+                  'assignment_mode']
+        return XendBase.getAttrRO() + attrRO
+
+    def getAttrRW(self):
+        attrRW = []
+        return XendBase.getAttrRW() + attrRW
+
+    def getAttrInst(self):
+        attrInst = ['VM',
+                    'virtual_host',
+                    'assignment_mode']
+        return XendBase.getAttrInst() + attrInst
+
+    def getMethods(self):
+        methods = ['destroy']
+        return XendBase.getMethods() + methods
+
+    def getFuncs(self):
+        funcs = ['create']
+        return XendBase.getFuncs() + funcs
+
+    getClass    = classmethod(getClass)
+    getAttrRO   = classmethod(getAttrRO)
+    getAttrRW   = classmethod(getAttrRW)
+    getAttrInst = classmethod(getAttrInst)
+    getMethods  = classmethod(getMethods)
+    getFuncs    = classmethod(getFuncs)
+    def create(self, dscsi_HBA_struct):
+
+        # Check if VM is valid
+        xendom = XendDomain.instance()
+        if not xendom.is_valid_vm(dscsi_HBA_struct['VM']):
+            raise InvalidHandleError('VM', dscsi_HBA_struct['VM'])
+        dom = xendom.get_vm_by_uuid(dscsi_HBA_struct['VM'])
+
+        # Check if PSCSI_HBA is valid
+        xennode = XendNode.instance()
+        pscsi_HBA_uuid = xennode.get_pscsi_HBA_by_uuid(dscsi_HBA_struct['PSCSI_HBA'])
+        if not pscsi_HBA_uuid:
+            raise InvalidHandleError('PSCSI_HBA', dscsi_HBA_struct['PSCSI_HBA'])
+
+        # Assign PSCSI_HBA and PSCSIs to VM
+        try:
+            dscsi_HBA_ref = XendTask.log_progress(0, 100, \
+                                                  dom.create_dscsi_HBA, \
+                                                  dscsi_HBA_struct)
+        except XendError, e:
+            log.exception("Error in create_dscsi_HBA")
+            raise
+
+        return dscsi_HBA_ref
+
+    create = classmethod(create)
+
+    def get_by_VM(cls, VM_ref):
+        result = []
+        for dscsi_HBA in XendAPIStore.get_all("DSCSI_HBA"):
+            if dscsi_HBA.get_VM() == VM_ref:
+                result.append(dscsi_HBA.get_uuid())
+        return result
+
+    get_by_VM = classmethod(get_by_VM)
+
+    def __init__(self, uuid, record):
+        XendBase.__init__(self, uuid, record)
+        self.virtual_host = record['virtual_host']
+        self.assignment_mode = record['assignment_mode']
+
+    def get_VM(self):
+        return self.VM
+
+    def get_PSCSI_HBAs(self):
+        PSCSIs = []
+        uuid = self.get_uuid()
+        for dscsi in XendAPIStore.get_all('DSCSI'):
+            if dscsi.get_VM() == self.VM and dscsi.get_HBA() == uuid:
+                PSCSIs.append(dscsi.get_PSCSI())
+        PSCSI_HBAs = []
+        for pscsi_uuid in PSCSIs:
+            pscsi_HBA_uuid = XendAPIStore.get(pscsi_uuid, 'PSCSI').get_HBA()
+            if not pscsi_HBA_uuid in PSCSI_HBAs:
+                PSCSI_HBAs.append(pscsi_HBA_uuid)
+        return PSCSI_HBAs
+
+    def get_DSCSIs(self):
+        DSCSIs = []
+        uuid = self.get_uuid()
+        for dscsi in XendAPIStore.get_all('DSCSI'):
+            if dscsi.get_VM() == self.VM and dscsi.get_HBA() == uuid:
+                DSCSIs.append(dscsi.get_uuid())
+        return DSCSIs
+
+    def get_virtual_host(self):
+        return self.virtual_host
+
+    def get_assignment_mode(self):
+        return self.assignment_mode
+
+    def destroy(self):
+        xendom = XendDomain.instance()
+        dom = xendom.get_vm_by_uuid(self.get_VM())
+        if not dom:
+            raise InvalidHandleError("VM", self.get_VM())
+        XendTask.log_progress(0, 100, \
+                              dom.destroy_dscsi_HBA, \
+                              self.get_uuid())
+
index 91c3fd728d9f30d46deaa5be2c426d72a45ca32a..bc9f553f09e701d2b2e7a5a6df3820fcff07907f 100644 (file)
@@ -68,7 +68,7 @@ from xen.xend import XendAPIStore
 from xen.xend.XendPPCI import XendPPCI
 from xen.xend.XendDPCI import XendDPCI
 from xen.xend.XendPSCSI import XendPSCSI
-from xen.xend.XendDSCSI import XendDSCSI
+from xen.xend.XendDSCSI import XendDSCSI, XendDSCSI_HBA
 
 MIGRATE_TIMEOUT = 30.0
 BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
@@ -3752,6 +3752,9 @@ class XendDomainInfo:
     def get_dscsis(self):
         return XendDSCSI.get_by_VM(self.info.get('uuid'))
 
+    def get_dscsi_HBAs(self):
+        return XendDSCSI_HBA.get_by_VM(self.info.get('uuid'))
+
     def create_vbd(self, xenapi_vbd, vdi_image_path):
         """Create a VBD using a VDI from XendStorageRepository.
 
@@ -3986,12 +3989,59 @@ class XendDomainInfo:
         else:
             try:
                 self.device_configure(target_vscsi_sxp)
-
             except Exception, exn:
+                log.exception('create_dscsi: %s', exn)
                 raise XendError('Failed to create device')
 
         return dscsi_uuid
 
+    def create_dscsi_HBA(self, xenapi_dscsi):
+        """Create scsi devices from the passed struct in Xen API format.
+
+        @param xenapi_dscsi: DSCSI_HBA struct from Xen API
+        @rtype: string
+        @return: UUID
+        """
+
+        dscsi_HBA_uuid = uuid.createString()
+
+        # Convert xenapi to sxp
+        feature_host = xenapi_dscsi.get('assignment_mode', 'HOST') == 'HOST' and 1 or 0
+        target_vscsi_sxp = \
+            ['vscsi',
+                ['feature-host', feature_host],
+                ['uuid', dscsi_HBA_uuid],
+            ]
+        pscsi_HBA = XendAPIStore.get(xenapi_dscsi.get('PSCSI_HBA'), 'PSCSI_HBA')
+        devid = pscsi_HBA.get_physical_host()
+        for pscsi_uuid in pscsi_HBA.get_PSCSIs():
+            pscsi = XendAPIStore.get(pscsi_uuid, 'PSCSI')
+            pscsi_HCTL = pscsi.get_physical_HCTL()
+            dscsi_uuid = uuid.createString()
+            dev = \
+                ['dev',
+                    ['devid', devid],
+                    ['p-devname', pscsi.get_dev_name()],
+                    ['p-dev', pscsi_HCTL],
+                    ['v-dev', pscsi_HCTL],
+                    ['state', xenbusState['Initialising']],
+                    ['uuid', dscsi_uuid]
+                ]
+            target_vscsi_sxp.append(dev)
+
+        if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING:
+            if not self.info.device_add('vscsi', cfg_sxp = target_vscsi_sxp):
+                raise XendError('Failed to create device')
+            xen.xend.XendDomain.instance().managed_config_save(self)
+        else:
+            try:
+                self.device_configure(target_vscsi_sxp)
+            except Exception, exn:
+                log.exception('create_dscsi_HBA: %s', exn)
+                raise XendError('Failed to create device')
+
+        return dscsi_HBA_uuid
+
 
     def change_vdi_of_vbd(self, xenapi_vbd, vdi_image_path):
         """Change current VDI with the new VDI.
@@ -4121,10 +4171,41 @@ class XendDomainInfo:
         else:
             try:
                 self.device_configure(target_vscsi_sxp)
-
             except Exception, exn:
+                log.exception('destroy_dscsi: %s', exn)
                 raise XendError('Failed to destroy device')
 
+    def destroy_dscsi_HBA(self, dev_uuid):
+        dscsi_HBA = XendAPIStore.get(dev_uuid, 'DSCSI_HBA')
+        devid = dscsi_HBA.get_virtual_host()
+        cur_vscsi_sxp = self._getDeviceInfo_vscsi(devid)
+        feature_host = sxp.child_value(cur_vscsi_sxp, 'feature-host')
+
+        if self._stateGet() != XEN_API_VM_POWER_STATE_RUNNING:
+            new_vscsi_sxp = ['vscsi', ['feature-host', feature_host]]
+            self.info.device_update(dev_uuid, new_vscsi_sxp)
+            del self.info['devices'][dev_uuid]
+            xen.xend.XendDomain.instance().managed_config_save(self)
+        else:
+            # If feature_host is 1, all devices are destroyed by just
+            # one reconfiguration.
+            # If feature_host is 0, we should reconfigure all devices
+            # one-by-one to destroy all devices.
+            # See reconfigureDevice@VSCSIController. 
+            for dev in sxp.children(cur_vscsi_sxp, 'dev'):
+                target_vscsi_sxp = [
+                    'vscsi',
+                    dev + [['state', xenbusState['Closing']]],
+                    ['feature-host', feature_host]
+                ]
+                try:
+                    self.device_configure(target_vscsi_sxp)
+                except Exception, exn:
+                    log.exception('destroy_dscsi_HBA: %s', exn)
+                    raise XendError('Failed to destroy device')
+                if feature_host:
+                    break
+
     def destroy_xapi_instances(self):
         """Destroy Xen-API instances stored in XendAPIStore.
         """
@@ -4153,6 +4234,10 @@ class XendDomainInfo:
         for dscsi_uuid in XendDSCSI.get_by_VM(self.info.get('uuid')):
             XendAPIStore.deregister(dscsi_uuid, "DSCSI")
             
+        # Destroy DSCSI_HBA instances.
+        for dscsi_HBA_uuid in XendDSCSI_HBA.get_by_VM(self.info.get('uuid')):
+            XendAPIStore.deregister(dscsi_HBA_uuid, "DSCSI_HBA")
+            
     def has_device(self, dev_class, dev_uuid):
         return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
 
index 8a7b6cd18bb2dab7df5cdda0e75a05e54f81de38..0fbefef6f8327335caaa53f10d451f05b8c716c0 100644 (file)
@@ -41,7 +41,7 @@ from XendNetwork import *
 from XendStateStore import XendStateStore
 from XendMonitor import XendMonitor
 from XendPPCI import XendPPCI
-from XendPSCSI import XendPSCSI
+from XendPSCSI import XendPSCSI, XendPSCSI_HBA
 
 class XendNode:
     """XendNode - Represents a Domain 0 Host."""
@@ -299,7 +299,7 @@ class XendNode:
             XendPPCI(ppci_uuid, ppci_record)
 
     def _init_PSCSIs(self):
-        # Initialise PSCSIs
+        # Initialise PSCSIs and PSCSI_HBAs
         saved_pscsis = self.state_store.load_state('pscsi')
         saved_pscsi_table = {}
         if saved_pscsis:
@@ -309,12 +309,52 @@ class XendNode:
                 except KeyError:
                     pass
 
+        saved_pscsi_HBAs = self.state_store.load_state('pscsi_HBA')
+        saved_pscsi_HBA_table = {}
+        if saved_pscsi_HBAs:
+            for pscsi_HBA_uuid, pscsi_HBA_record in saved_pscsi_HBAs.items():
+                try:
+                    physical_host = int(pscsi_HBA_record['physical_host'])
+                    saved_pscsi_HBA_table[physical_host] = pscsi_HBA_uuid
+                except (KeyError, ValueError):
+                    pass
+
+        pscsi_table = {}
+        pscsi_HBA_table = {}
+
         for pscsi_record in vscsi_util.get_all_scsi_devices():
-            if pscsi_record['scsi_id']:
-                # If saved uuid exists, use it. Otherwise create one.
-                pscsi_uuid = saved_pscsi_table.get(pscsi_record['scsi_id'],
-                                                   uuid.createString())
-                XendPSCSI(pscsi_uuid, pscsi_record)
+            scsi_id = pscsi_record['scsi_id']
+            if scsi_id:
+                saved_HBA_uuid = None
+
+                pscsi_uuid = saved_pscsi_table.get(scsi_id, None)
+                if pscsi_uuid is None:
+                    pscsi_uuid = uuid.createString()
+                    saved_pscsi_table[scsi_id] = pscsi_uuid
+                else:
+                    saved_HBA_uuid = saved_pscsis[pscsi_uuid].get('HBA', None)
+
+                physical_host = int(pscsi_record['physical_HCTL'].split(':')[0])
+                if pscsi_HBA_table.has_key(physical_host):
+                    pscsi_HBA_uuid = pscsi_HBA_table[physical_host]
+                elif saved_pscsi_HBA_table.has_key(physical_host):
+                    pscsi_HBA_uuid = saved_pscsi_HBA_table[physical_host]
+                    pscsi_HBA_table[physical_host] = pscsi_HBA_uuid
+                else:
+                    pscsi_HBA_uuid = uuid.createString()
+                    pscsi_HBA_table[physical_host] = pscsi_HBA_uuid
+
+                if saved_HBA_uuid is not None and \
+                   saved_HBA_uuid != pscsi_HBA_uuid:
+                    log.debug('The PSCSI(%s) host number was changed', scsi_id)
+                pscsi_record['HBA'] = pscsi_HBA_uuid
+                pscsi_table[pscsi_uuid] = pscsi_record
+
+        for pscsi_uuid, pscsi_record in pscsi_table.items():
+            XendPSCSI(pscsi_uuid, pscsi_record)
+
+        for physical_host, pscsi_HBA_uuid in pscsi_HBA_table.items():
+            XendPSCSI_HBA(pscsi_HBA_uuid, {'physical_host': physical_host})
 
 
     def add_network(self, interface):
@@ -389,14 +429,32 @@ class XendNode:
                 except KeyError:
                     pass
 
-        # Initialise the PSCSI
+        saved_pscsi_HBAs = self.state_store.load_state('pscsi_HBA')
+        saved_pscsi_HBA_table = {}
+        if saved_pscsi_HBAs:
+            for saved_HBA_uuid, saved_HBA_record in saved_pscsi_HBAs.items():
+                try:
+                    physical_host = int(saved_HBA_record['physical_host'])
+                    saved_pscsi_HBA_table[physical_host] = saved_HBA_uuid
+                except (KeyError, ValueError):
+                    pass
+
+        # Initialise the PSCSI and the PSCSI_HBA
         pscsi_record = vscsi_util.get_scsi_device(add_HCTL)
         if pscsi_record and pscsi_record['scsi_id']:
             pscsi_uuid = saved_pscsi_table.get(pscsi_record['scsi_id'], None)
             if pscsi_uuid is None:
+                physical_host = int(add_HCTL.split(':')[0])
+                pscsi_HBA_uuid = saved_pscsi_HBA_table.get(physical_host, None)
+                if pscsi_HBA_uuid is None:
+                    pscsi_HBA_uuid = uuid.createString()
+                    XendPSCSI_HBA(pscsi_HBA_uuid, {'physical_host': physical_host})
+                pscsi_record['HBA'] = pscsi_HBA_uuid
+
                 pscsi_uuid = uuid.createString()
                 XendPSCSI(pscsi_uuid, pscsi_record)
                 self.save_PSCSIs()
+                self.save_PSCSI_HBAs()
 
 
     def remove_PSCSI(self, rem_HCTL):
@@ -410,6 +468,14 @@ class XendNode:
                 pscsi_ref = XendPSCSI.get_by_HCTL(rem_HCTL)
                 XendAPIStore.get(pscsi_ref, "PSCSI").destroy()
                 self.save_PSCSIs()
+
+                physical_host = int(rem_HCTL.split(':')[0])
+                pscsi_HBA_ref = XendPSCSI_HBA.get_by_physical_host(physical_host)
+                if pscsi_HBA_ref:
+                    if not XendAPIStore.get(pscsi_HBA_ref, 'PSCSI_HBA').get_PSCSIs():
+                        XendAPIStore.get(pscsi_HBA_ref, 'PSCSI_HBA').destroy()
+                self.save_PSCSI_HBAs()
+
                 return
 
 
@@ -472,6 +538,14 @@ class XendNode:
             return pscsi_uuid
         return None
 
+    def get_PSCSI_HBA_refs(self):
+        return XendPSCSI_HBA.get_all()
+
+    def get_pscsi_HBA_by_uuid(self, pscsi_HBA_uuid):
+        if pscsi_HBA_uuid in self.get_PSCSI_HBA_refs():
+            return pscsi_HBA_uuid
+        return None
+
 
     def save(self):
         # save state
@@ -487,6 +561,7 @@ class XendNode:
         self.save_SRs()
         self.save_PPCIs()
         self.save_PSCSIs()
+        self.save_PSCSI_HBAs()
 
     def save_PIFs(self):
         pif_records = dict([(pif_uuid, XendAPIStore.get(
@@ -523,6 +598,12 @@ class XendNode:
                             for pscsi_uuid in XendPSCSI.get_all()])
         self.state_store.save_state('pscsi', pscsi_records)
 
+    def save_PSCSI_HBAs(self):
+        pscsi_HBA_records = dict([(pscsi_HBA_uuid, XendAPIStore.get(
+                                      pscsi_HBA_uuid, "PSCSI_HBA").get_record())
+                                for pscsi_HBA_uuid in XendPSCSI_HBA.get_all()])
+        self.state_store.save_state('pscsi_HBA', pscsi_HBA_records)
+
     def shutdown(self):
         return 0
 
index 9026c6185581239e733647184a8928d556857232..e50aa5bec9c76df11eddbda7354b39ce38805012 100644 (file)
@@ -33,6 +33,7 @@ class XendPSCSI(XendBase):
                   'physical_target',
                   'physical_lun',
                   'physical_HCTL',
+                  'HBA',
                   'vendor_name',
                   'model',
                   'type_id',
@@ -77,6 +78,7 @@ class XendPSCSI(XendBase):
 
     def __init__(self, uuid, record):
         self.physical_HCTL = record['physical_HCTL']
+        self.physical_HBA = record['HBA']
         self.vendor_name = record['vendor_name']
         self.model = record['model']
         self.type_id = record['type_id']
@@ -114,6 +116,9 @@ class XendPSCSI(XendBase):
     def get_physical_HCTL(self):
         return self.physical_HCTL
 
+    def get_HBA(self):
+        return self.physical_HBA
+
     def get_vendor_name(self):
         return self.vendor_name
 
@@ -141,3 +146,66 @@ class XendPSCSI(XendBase):
     def get_scsi_level(self):
         return self.scsi_level
 
+
+class XendPSCSI_HBA(XendBase):
+    """Representation of a physical SCSI HBA."""
+
+    def getClass(self):
+        return "PSCSI_HBA"
+
+    def getAttrRO(self):
+        attrRO = ['host',
+                  'physical_host',
+                  'PSCSIs']
+        return XendBase.getAttrRO() + attrRO
+
+    def getAttrRW(self):
+        attrRW = []
+        return XendBase.getAttrRW() + attrRW
+
+    def getAttrInst(self):
+        attrInst = []
+        return XendBase.getAttrInst() + attrInst
+
+    def getMethods(self):
+        methods = []
+        return XendBase.getMethods() + methods
+
+    def getFuncs(self):
+        funcs = []
+        return XendBase.getFuncs() + funcs
+
+    getClass    = classmethod(getClass)
+    getAttrRO   = classmethod(getAttrRO)
+    getAttrRW   = classmethod(getAttrRW)
+    getAttrInst = classmethod(getAttrInst)
+    getMethods  = classmethod(getMethods)
+    getFuncs    = classmethod(getFuncs)
+    def get_by_physical_host(self, physical_host):
+        for pscsi_HBA in XendAPIStore.get_all('PSCSI_HBA'):
+            if pscsi_HBA.get_physical_host() == physical_host:
+                return pscsi_HBA.get_uuid()
+        return None
+
+    get_by_physical_host = classmethod(get_by_physical_host)
+
+    def __init__(self, uuid, record):
+        self.physical_host = record['physical_host']
+        XendBase.__init__(self, uuid, record)
+
+    def get_host(self):
+        from xen.xend import XendNode
+        return XendNode.instance().get_uuid()
+
+    def get_physical_host(self):
+        return self.physical_host
+
+    def get_PSCSIs(self):
+        PSCSIs = []
+        uuid = self.get_uuid()
+        for pscsi in XendAPIStore.get_all('PSCSI'):
+            if pscsi.get_HBA() == uuid:
+                PSCSIs.append(pscsi.get_uuid())
+        return PSCSIs
+
index 93ea47e52f1f0ca0f68af2a5c931e12a9b8ac3d3..32529bd2b490d811529ff632963dae0372409d13 100644 (file)
                  key             CDATA #IMPLIED>
 
 <!ELEMENT vscsi  EMPTY>
-<!ATTLIST vscsi  p-dev           CDATA #REQUIRED
-                 v-dev           CDATA #REQUIRED>
+<!ATTLIST vscsi  feature-host    CDATA #REQUIRED
+                 p-dev           CDATA #IMPLIED
+                 v-dev           CDATA #IMPLIED
+                 devid           CDATA #IMPLIED>
 
 <!ELEMENT console (other_config*)>
 <!ATTLIST console protocol       (vt100|rfb|rdp) #REQUIRED>
index 52b13a29d8eea29390562bd21308463e12a39261..c62c86b5c6f5febb3650414c7896cccfd6e1be45 100644 (file)
@@ -801,9 +801,6 @@ def configure_vscsis(config_devs, vals):
 
         feature_host = 0
         if v_dev == 'host':
-            if serverType == SERVER_XEN_API:
-                # TODO
-                raise ValueError("SCSI devices assignment by HBA is not implemeted")
             feature_host = 1
             scsi_info = []
             devid = get_devid(p_hctl)
index e570f8eebe88a6cbc594c757a18a628e27d423c7..4ba6ab8d1438cb1d06807cdd406c08dea8daddb6 100644 (file)
@@ -2672,9 +2672,6 @@ def parse_scsi_configuration(p_scsi, v_hctl, state):
     if p_scsi is not None:
         # xm scsi-attach
         if v_hctl == "host":
-            if serverType == SERVER_XEN_API:
-                # TODO
-                raise OptionError("SCSI devices assignment by HBA is not implemeted")
             host_mode = 1
             scsi_devices = vscsi_util.vscsi_get_scsidevices()
         elif len(v_hctl.split(':')) != 4:
@@ -2718,21 +2715,39 @@ def xm_scsi_attach(args):
     if serverType == SERVER_XEN_API:
 
         scsi_dev = sxp.children(scsi, 'dev')[0]
-        p_hctl = sxp.child_value(scsi_dev, 'p-dev')
-        target_ref = None
-        for pscsi_ref in server.xenapi.PSCSI.get_all():
-            if p_hctl == server.xenapi.PSCSI.get_physical_HCTL(pscsi_ref):
-                target_ref = pscsi_ref
-                break
-        if target_ref is None:
-            raise OptionError("Cannot find device '%s'" % p_scsi)
 
-        dscsi_record = {
-            "VM":           get_single_vm(dom),
-            "PSCSI":        target_ref,
-            "virtual_HCTL": v_hctl
-        }
-        server.xenapi.DSCSI.create(dscsi_record)
+        if sxp.child_value(scsi, 'feature-host'):
+            p_host = sxp.child_value(scsi_dev, 'devid')
+            target_ref = None
+            for pscsi_ref in server.xenapi.PSCSI_HBA.get_all():
+                if p_host == int(server.xenapi.PSCSI_HBA.get_physical_host(pscsi_ref)):
+                    target_ref = pscsi_ref
+                    break
+            if target_ref is None:
+                raise OptionError("Cannot find device '%s'" % p_scsi)
+
+            dscsi_record = {
+                "VM":              get_single_vm(dom),
+                "PSCSI_HBA":       target_ref,
+                "assignment_mode": "HOST"
+            }
+            server.xenapi.DSCSI_HBA.create(dscsi_record)
+        else:
+            p_hctl = sxp.child_value(scsi_dev, 'p-dev')
+            target_ref = None
+            for pscsi_ref in server.xenapi.PSCSI.get_all():
+                if p_hctl == server.xenapi.PSCSI.get_physical_HCTL(pscsi_ref):
+                    target_ref = pscsi_ref
+                    break
+            if target_ref is None:
+                raise OptionError("Cannot find device '%s'" % p_scsi)
+
+            dscsi_record = {
+                "VM":           get_single_vm(dom),
+                "PSCSI":        target_ref,
+                "virtual_HCTL": v_hctl
+            }
+            server.xenapi.DSCSI.create(dscsi_record)
 
     else:
         if len(args) == 4:
@@ -2894,7 +2909,11 @@ def xm_scsi_detach(args):
         if target_ref is None:
             raise OptionError("Device %s not assigned" % v_hctl)
 
-        server.xenapi.DSCSI.destroy(target_ref)
+        target_HBA_ref = server.xenapi.DSCSI.get_HBA(target_ref)
+        if server.xenapi.DSCSI_HBA.get_assignment_mode(target_HBA_ref) == "HOST":
+            server.xenapi.DSCSI_HBA.destroy(target_HBA_ref)
+        else:
+            server.xenapi.DSCSI.destroy(target_ref)
 
     else:
         server.xend.domain.device_configure(dom, scsi)
index 13e0af132f394f8303f33bde4fbba8094f0e72dc..9cfdb87d7ead3ea52b45c744db7664b929f0b9bd 100644 (file)
@@ -567,25 +567,48 @@ class xenapi_create:
     def create_scsi(self, vm_ref, scsi):
         log(DEBUG, "create_scsi")
 
-        target_ref = None
-        for pscsi_ref in server.xenapi.PSCSI.get_all():
-            if scsi.attributes["p-dev"].value == server.xenapi.PSCSI.get_physical_HCTL(pscsi_ref):
-                target_ref = pscsi_ref
-                break
-        if target_ref is None:
-            log(DEBUG, "create_scsi: scsi device not found")
-            return None
+        if scsi.attributes["feature-host"].value == "True":
+            target_HBA_ref = None
+            for pscsi_HBA_ref in server.xenapi.PSCSI_HBA.get_all():
+                if int(scsi.attributes["devid"].value) == \
+                   int(server.xenapi.PSCSI_HBA.get_physical_host(pscsi_HBA_ref)):
+                    target_HBA_ref = pscsi_HBA_ref
+                    break
+            if target_HBA_ref is None:
+                log(DEBUG, "create_scsi: scsi device not found")
+                return None
+
+            dscsi_record = {
+                "VM":
+                    vm_ref,
+                "PSCSI_HBA":
+                    target_HBA_ref,
+                "assignment_mode":
+                    "HOST"
+            }
 
-        dscsi_record = {
-            "VM":
-                vm_ref,
-            "PSCSI":
-                target_ref,
-            "virtual_HCTL":
-                scsi.attributes["v-dev"].value
-        }
+            return server.xenapi.DSCSI_HBA.create(dscsi_record)
+        else:
+            target_ref = None
+            for pscsi_ref in server.xenapi.PSCSI.get_all():
+                if scsi.attributes["p-dev"].value == \
+                   server.xenapi.PSCSI.get_physical_HCTL(pscsi_ref):
+                    target_ref = pscsi_ref
+                    break
+            if target_ref is None:
+                log(DEBUG, "create_scsi: scsi device not found")
+                return None
+
+            dscsi_record = {
+                "VM":
+                    vm_ref,
+                "PSCSI":
+                    target_ref,
+                "virtual_HCTL":
+                    scsi.attributes["v-dev"].value
+            }
 
-        return server.xenapi.DSCSI.create(dscsi_record)
+            return server.xenapi.DSCSI.create(dscsi_record)
 
 def get_child_by_name(exp, childname, default = None):
     try:
@@ -981,15 +1004,23 @@ class sxp2xml:
         scsis = []
 
         for scsi_sxp in scsis_sxp:
+            feature_host = sxp.child_value(scsi_sxp, "feature-host")
             for dev_sxp in sxp.children(scsi_sxp, "dev"):
                 scsi = document.createElement("vscsi")
 
-                scsi.attributes["p-dev"] \
-                    = get_child_by_name(dev_sxp, "p-dev")
-                scsi.attributes["v-dev"] \
-                    = get_child_by_name(dev_sxp, "v-dev")
-
-                scsis.append(scsi)
+                scsi.attributes["feature-host"] \
+                    = feature_host and "True" or "False"
+                if feature_host:
+                    scsi.attributes["devid"] \
+                        = str(get_child_by_name(dev_sxp, "devid"))
+                    scsis.append(scsi)
+                    break
+                else:
+                    scsi.attributes["p-dev"] \
+                        = get_child_by_name(dev_sxp, "p-dev")
+                    scsi.attributes["v-dev"] \
+                        = get_child_by_name(dev_sxp, "v-dev")
+                    scsis.append(scsi)
 
         return scsis